package cn.dengta.webapp.trade.service;

import java.util.*;
import javax.annotation.*;

import cn.dengta.common.context.I18N;
import cn.dengta.common.model.ErrorMessageException;
import cn.dengta.common.model.Messager;
import cn.dengta.common.util.Common;
import cn.dengta.common.web.Page;
import cn.dengta.context.model.*;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.trade.dao.UserRelationDao;
import cn.dengta.webapp.trade.dto.RelationDTO;
import cn.dengta.webapp.user.cache.UserCaches;
import cn.dengta.webapp.user.entity.Relation;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import me.codeplayer.util.CollectionUtil;
import me.codeplayer.util.NumberUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import static cn.dengta.webapp.user.entity.Relation.*;

/**
 * 用户相关 服务实现类【公共缓存】
 *
 * @author lgy
 * @since 2022-03-07
 */
@Service
public class UserRelationService extends BaseServiceImpl<Relation, UserRelationDao, Long> {

	@Resource
	RedisTemplate<String, Object> redisTemplate;
	@Resource
	StringRedisTemplate stringRedisTemplate;
	@Resource
	UserCaches userCaches;
	@Resource
	UserIntimacyService userIntimacyService;

	/** 用户关系数据（ZSet） */
	static final String redisKey = "relations";

	/**
	 * 好友是否被拉黑
	 */
	public void assertNonBlock(@Nonnull Long userId, @Nonnull Long toUserId) {
		assertNonBlock(userId, toUserId, true);
	}

	@Nullable
	public <T> Messager<T> assertNonBlock(@Nonnull Long userId, @Nonnull Long toUserId, boolean throwMsg) {
		RelationDTO dto = getValidTarget(userId, toUserId);
		Messager<T> msger = null;
		if (dto.isAnyBlock()) {
			Language language = userCaches.get(userId).getLanguage();
			String msg = dto.isBlock(toUserId) ? language.msg(UserI18nKey.STOP_OPERATION_FOR_BLOCK) : language.msg(UserI18nKey.STOP_OPERATION_FOR_BLOCKED);
			msger = Messager.status(MessagerStatus.EXIST_BLOCK, msg);
			if (throwMsg) {
				throw new ErrorMessageException(msger);
			}
		}
		return msger;
	}

	/**
	 * 修改缓存
	 */
	public void changeRelationCache(@Nonnull Relation input) {
		final RelationDTO dto = RelationDTO.of(input);
		stringRedisTemplate.opsForZSet().add(redisKey, input.redisMember(), dto.getBizFlag());
	}

	/**
	 * 查询用户关系
	 */
	@Nonnull
	public RelationDTO getValidTarget(@Nonnull Long userId, @Nonnull Long toUserId) {
		if (userId.equals(toUserId)) {
			return RelationDTO.NONE;
		}
		final String member = Common.sortKey(userId, toUserId, Common.LINE_SEP);
		final Double score = stringRedisTemplate.opsForZSet().score(redisKey, member);
		if (score == null) {
			return RelationDTO.of(userId, toUserId, 0);
			/*
			final String field = userId + Common.LINE_SEP + toUserId;
			String json = stringRedisTemplate.<String, String>opsForHash().get(RedisKey.USER_RELATION_LOADER, field);
			if (StringUtil.isEmpty(json)) {
				return RelationDTO.of(userId, toUserId, 0);
			}
			final Relation relation = JSON.parseObject(json, Relation.class, Feature.SupportAutoType);
			final RelationDTO dto = RelationDTO.of(relation);
			final long bizFlag = dto.getBizFlag();
			if (bizFlag != 0) {
				stringRedisTemplate.opsForZSet().add(redisKey, member, bizFlag);
			}
			// 移除旧的关系数据
			stringRedisTemplate.<String, String>opsForHash().delete(RedisKey.USER_RELATION_LOADER, field, toUserId + Common.LINE_SEP + userId);
			return dto;
			*/
		}
		return RelationDTO.of(userId, toUserId, score.longValue());
	}

	/**
	 * 查询拨通率
	 */
	public Map<Long, Integer> findRedisCallRate(List<String> userIds) {
		List<Integer> list = redisTemplate.<String, Integer>opsForHash().multiGet(RedisKey.IM_CALL_RATE, userIds);
		if (list.isEmpty()) {
			return Collections.emptyMap();
		}
		final int size = list.size();
		final Map<Long, Integer> map = CollectionUtil.newHashMap(size);
		for (int i = 0; i < size; i++) {
			map.put(NumberUtil.getLong(userIds.get(i)), NumberUtil.getInt(list.get(i), 100));
		}
		return map;
	}

	/**
	 * 查询拨通率
	 */
	public Integer getRedisCallRate(Long userId) {
		Integer callRate = redisTemplate.<String, Integer>opsForHash().get(RedisKey.IM_CALL_RATE, userId.toString());
		return NumberUtil.getInt(callRate, 100);
	}

	/**
	 * 获取亲密度钻石
	 */
	public Integer getIntimacyCoin(@Nonnull Long userId, @Nonnull Long toUserId) {
//		Pair<Long, Long> pair = Common.sort(userId, toUserId);
//		return userIntimacyService.findScoreByUser(pair.getLeft(), pair.getRight());
		return NumberUtil.getInt(redisTemplate.opsForZSet().score(RedisKey.INTIMACY_COMMON, Common.sortKey(userId, toUserId, null)), 0);
	}

	/**
	 * 获取勿扰模式用户，如果过期自动删除
	 */
	public long getDisturbIfCan(@Nonnull Long userId) {
		ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
		Double score = opsForZSet.score(RedisKey.IM_CALL_DISTURB, userId.toString());
		if (score != null && score.longValue() <= System.currentTimeMillis()) {
			opsForZSet.remove(RedisKey.IM_CALL_DISTURB, userId.toString());
			score = null;
		}
		return NumberUtil.getLong(score, 0L);
	}

	@Nullable
	public <T> Messager<T> assertNonDisturb(@Nonnull Long userId, boolean throwMsg) {
		long disturb = getDisturbIfCan(userId);
		Messager<T> msger = null;
		if (disturb > 0) {
			msger = Messager.error(I18N.msg(ImI18nKey.SET_DISTURB));
			if (throwMsg) {
				throw new ErrorMessageException(msger);
			}
		}
		return msger;
	}

	/**
	 * 判断俩用户是否存在拉黑
	 */
	public boolean isBlock(Long userId, Long toUserId) {
		final RelationDTO dto = getValidTarget(userId, toUserId);
		return dto.isAnyBlock();
	}

	/**
	 * 返回与指定用户存在拉黑关系的用户ID集合（主动拉黑对方 或 被对方拉黑 都算）
	 */
	public List<Long> findAnyBlockUserIdsFor(Long userId) {
		return baseDao.findAnyBlockUserIdsFor(userId);
	}

	public List<Relation> findAnyBlock(@Nonnull Long userId, @Nullable Long toUserId) {
		return baseDao.selectList(new QueryWrapper<Relation>()
				.eq(USER_ID, userId)
				.eq(toUserId != null, TO_USER_ID, toUserId)
				.in(STATUS, RelationStatus.BLOCK.value, RelationStatus.TO_BLOCK.value));
	}

	public Long getFollowCount(Long userId) {
		return baseDao.getFollowCount(userId);
	}

	public Page<Long> findLiveAnchorUserIdsFor(Long userId, Page<?> page) {
		return baseDao.findLiveAnchorUserIdsFor(userId, page);
	}

	public Long getBlacklistCount(@Nonnull Long userId) {
		return baseDao.selectCount(new QueryWrapper<Relation>()
				.eq(USER_ID, userId)
				.eq(STATUS, RelationStatus.BLOCK));
	}

}
